﻿using System.Collections.Generic;
using System.Linq;
using CardPlay.Model.CardModel;
using CardPlay.Model.Primitives;
using CardPlay.Tests.Common;
using NUnit.Framework;
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;

namespace CardPlay.Tests.Model.CardModel
{
    [Binding]
    public class Steps
    {
        private const string Result = "Result";

        private static List<Card> Deck
        {
            get { return DeckFactory.CreateStandardDeck().ToList(); }
        }

        [Given(@"the build rule (.+)")]
        public void GivenTheBuildRule(CardFaceSequenceSpecification rule)
        {
            ScenarioContext.Current.Set(rule);
        }

        [When(@"I attempt to match the suits (.+) (.+) using (.+)")]
        public void WhenICompareSuits(Suit suitA, Suit suitB, string method)
        {
            ScenarioContext.Current.Set(typeof(SuitSequence).InvokeStatic(method.ToMemberName(), suitA, suitB), Result);
        }

        [When(@"I attempt to match the ranks (.+) (.+) using (.+)")]
        public void WhenICompareRanks(Rank rankA, Rank rankB, string method)
        {
            ScenarioContext.Current.Set(typeof(RankSequence).InvokeStatic(method.ToMemberName(), rankA, rankB), Result);
        }

        [When(@"I attempt to play (.+) onto (.+)")]
        public void WhenIAttemptToPlay(CardFace card, CardFace nextCard)
        {
            var rule = ScenarioContext.Current.Get<CardFaceSequenceSpecification>();
            ScenarioContext.Current.Set(rule.IsSatisfiedBy(card, nextCard), Result);
        }

        [Then(@"the rank should be (.+)")]
        public void ThenTheRankShouldBe(Rank rank)
        {
            Assert.AreEqual(rank, ScenarioContext.Current.Get<CardFace>(Result).Rank);
        }

        [Then(@"the suit should be (.+)")]
        public void ThenTheSuitShouldBe(Suit suit)
        {
            Assert.AreEqual(suit, ScenarioContext.Current.Get<CardFace>(Result).Suit);
        }

        [Then(@"the (?:play|match|result) should be (.+)")]
        public void ThenTheResultShouldBe(bool expectedResult)
        {
            Assert.AreEqual(expectedResult, ScenarioContext.Current.Get<bool>(Result));
        }

        #region Dealing

        [Given(@"I have a standard unshuffled deck")]
        public void GivenIHaveAStandardUnshuffledDeck()
        {
            ScenarioContext.Current.Set(Deck);
        }

        [Given(@"I have stripped the deck to (.+)s")]
        public void GivenIHaveStrippedTheDeckTo(Rank rank)
        {
            ScenarioContext.Current.Set(Deck.Where(card => card.Rank == rank).ToList());
        }

        [Given(@"I have the following deal instructions:")]
        public void GivenIHaveTheFollowingDealInstructions(Table table)
        {
            var script = new DealScript();

            foreach (var row in table.Rows)
            {
                script.Deal(row["Deal"]).To(row["To"]);
            }

            ScenarioContext.Current.Set(script.Compile());
        }

        [When(@"I execute the instructions")]
        public void WhenIExecuteTheInstructions()
        {
            var deck = ScenarioContext.Current.Get<List<Card>>();
            var result = new List<DealResult>();

            var dealer = new Dealer(deck, 
                (card, location) => result.Add(new DealResult { Card = card.Face, Location = location }));

            dealer.Deal(ScenarioContext.Current.Get<IDealAction>());

            ScenarioContext.Current.Set(result, Result);
        }

        [Then(@"I should see the cards dealt as follows:")]
        public void ThenIShouldSeeTheCardsDealtAsFollows(Table expectedResult)
        {
            var actualResult = ScenarioContext.Current.Get<IEnumerable<DealResult>>(Result);
            expectedResult.CompareToSet(actualResult);
        }

        #endregion
    }
}
